//
// Created by 朕与将军解战袍 on 2022/3/13.
//

#include "ttf_info.h"

#ifdef TTF_RENDER_IMPLEMENT

void ttfRead(const void *src, void *des, uintptr_t size) {
    char *src_byte_ptr = (char*)src;
    char *des_byte_ptr = (char*)des;
    uintptr_t offset = size - 1;
    for (uintptr_t i = 0; i < size; i++) {
        des_byte_ptr[i] = src_byte_ptr[offset - i];
    }
}

int compareUintBigEndian(void *a_big_endian_ptr, void *b_big_endian_ptr, size_t size) {
    unsigned char *a_char_ptr = (unsigned char*)a_big_endian_ptr;
    unsigned char *b_char_ptr = (unsigned char*)b_big_endian_ptr;

    for (int i = 0; i < size; i++) {
        if (a_char_ptr[i] == b_char_ptr[i]) {
            continue;
        } else if (a_char_ptr[i] > b_char_ptr[i]) {
            return 1;
        } else {
            return -1;
        }
    }

    return 0;
}

int lowerBoundBigEndian(uint16_t *nums, uint16_t target, int len) {
    int left  = 0;
    int right = len - 1;
    // 搜索区间: [left, right]
    while (left <= right) {
        int mid = (right - left) / 2 + left;
        int result = TTF_COM_UINT(nums[mid], target);
        switch (result) {
            case -1:
                // 搜索区间 -> [mid+1, right]
                left = mid + 1;
                break;
            case 0:
            case 1:
                // 搜索区间 -> [left, mid-1]
                right = mid - 1;
                break;
        }
    }

    if (left >= len || nums[left] != target) {
        return -1;
    }

    return left;
}


__attribute__((unused)) int upperBoundBigEndian(uint16_t *nums, uint16_t target, int len) {
    int left = 0;
    int right = len - 1;
    while (left <= right) {
        int mid = (right - left) / 2 + left;
        int result = TTF_COM_UINT(nums[mid], target);
        switch (result) {
            case 1:
                right = mid - 1;
                break;
            case 0:
            case -1:
                left = mid + 1;
                break;
        }
    }

    if (right < 0 || nums[right] != target) {
        return -1;
    }

    return right;
}

TTFStatus initTTFInfo(void *ttfData, TTFInfo *ttfInfo) {
    TTFStatus status = SUCCESS;

    TTF_READ(ttfData, offsetof(OffsetSubtable, scalerType),
             ttfInfo->offsetSubtable.scalerType);
    if (ttfInfo->offsetSubtable.scalerType != 0x00010000) {
        return UNSUPPORTED_TTF_FORMAT;
    }
    TTF_READ(ttfData, offsetof(OffsetSubtable, numTables),
             ttfInfo->offsetSubtable.numTables);
    TTF_READ(ttfData, offsetof(OffsetSubtable, searchRange),
             ttfInfo->offsetSubtable.searchRange);
    TTF_READ(ttfData, offsetof(OffsetSubtable, entrySelector),
             ttfInfo->offsetSubtable.entrySelector);
    TTF_READ(ttfData, offsetof(OffsetSubtable, rangeShift),
             ttfInfo->offsetSubtable.rangeShift);

    ttfInfo->ttfData = ttfData;
    ttfInfo->rc = 0;

    moveToNThTableDir(ttfInfo, 0);

    return status;
}

TTFStatus freeTTFInfo(TTFInfo *ttfInfo) {
    if (ttfInfo->rc > 0) {
        return RC_NON_ZERO;
    }
    DEALLOCATE(ttfInfo->ttfData);
    return SUCCESS;
}

TTFStatus freeCreatedInfo(void *createdInfo) {
    // 减少引用计数
    ((TTFInfo *)createdInfo)->rc--;
    return SUCCESS;
}

TTFStatus moveToNThTableDir(TTFInfo *ttfInfo, size_t n) {
    if (n >= ttfInfo->offsetSubtable.numTables) {
        return OUT_OF_BOUNDARY;
    }
    size_t base = sizeof(TableDir) * n + sizeof(OffsetSubtable);
    TTF_READ(ttfInfo->ttfData, base + offsetof(TableDir, tag),
             ttfInfo->cacheTableDir.tag);
    TTF_READ(ttfInfo->ttfData, base + offsetof(TableDir, checkSum),
             ttfInfo->cacheTableDir.checkSum);
    TTF_READ(ttfInfo->ttfData, base + offsetof(TableDir, offset),
             ttfInfo->cacheTableDir.offset);
    TTF_READ(ttfInfo->ttfData, base + offsetof(TableDir, length),
             ttfInfo->cacheTableDir.length);

    return SUCCESS;
}

TTFStatus moveToSpecificTableDir(TTFInfo *ttfInfo, uint32_t tag) {
    for (int i = 0; i < ttfInfo->offsetSubtable.numTables; i++) {
        moveToNThTableDir(ttfInfo, i);
        if (ttfInfo->cacheTableDir.tag == tag) {
            return SUCCESS;
        }
    }
    return TABLE_NOT_FOUND;
}

TTFStatus getHeadTable(TTFInfo *ttfInfo, HeadTable *headTable) {
    TTFStatus status = moveToSpecificTableDir(ttfInfo, TAG_HEAD);
    if (TTF_ERR(status)) {
        return status;
    }

    void* ttfData = ttfInfo->ttfData + ttfInfo->cacheTableDir.offset;
    TTF_READ(ttfData, offsetof(HeadTable, magicNumber), headTable->magicNumber);
    if (headTable->magicNumber != HEAD_TABLE_MAGIC_NUM) {
        return INVALID_PARAMETER;
    }

    TTF_READ(ttfData, offsetof(HeadTable, version), headTable->version);
    TTF_READ(ttfData, offsetof(HeadTable, fontRevision), headTable->fontRevision);
    TTF_READ(ttfData, offsetof(HeadTable, checkSumAdjustment), headTable->checkSumAdjustment);
    TTF_READ(ttfData, offsetof(HeadTable, flags), headTable->flags);
    TTF_READ(ttfData, offsetof(HeadTable, unitsPerEm), headTable->unitsPerEm);
    TTF_READ(ttfData, offsetof(HeadTable, created), headTable->created);
    TTF_READ(ttfData, offsetof(HeadTable, modified), headTable->modified);
    TTF_READ(ttfData, offsetof(HeadTable, xMin), headTable->xMin);
    TTF_READ(ttfData, offsetof(HeadTable, yMin), headTable->yMin);
    TTF_READ(ttfData, offsetof(HeadTable, xMax), headTable->xMax);
    TTF_READ(ttfData, offsetof(HeadTable, yMax), headTable->yMax);
    TTF_READ(ttfData, offsetof(HeadTable, macStyle), headTable->macStyle);
    TTF_READ(ttfData, offsetof(HeadTable, lowestRecPPEM), headTable->lowestRecPPEM);
    TTF_READ(ttfData, offsetof(HeadTable, fontDirectionHint), headTable->fontDirectionHint);
    TTF_READ(ttfData, offsetof(HeadTable, indexToLocFormat), headTable->indexToLocFormat);
    TTF_READ(ttfData, offsetof(HeadTable, glyphDataFormat), headTable->glyphDataFormat);

    return status;
}

TTFStatus getMaxpTable(TTFInfo *ttfInfo, MaxpTable *maxpTable) {
    TTFStatus status = moveToSpecificTableDir(ttfInfo, TAG_MAXP);
    if (TTF_ERR(status)) {
        return status;
    }

    void *ttfData = ttfInfo->ttfData + ttfInfo->cacheTableDir.offset;
    TTF_READ(ttfData, offsetof(MaxpTable, version), maxpTable->version);
    TTF_READ(ttfData, offsetof(MaxpTable, maxZones), maxpTable->maxZones);
    if (maxpTable->version != 0x00010000) {
        return INVALID_PARAMETER;
    }

    TTF_READ(ttfData, offsetof(MaxpTable, numGlyphs), maxpTable->numGlyphs);
    TTF_READ(ttfData, offsetof(MaxpTable, maxPoints), maxpTable->maxPoints);
    TTF_READ(ttfData, offsetof(MaxpTable, maxContours), maxpTable->maxContours);
    TTF_READ(ttfData, offsetof(MaxpTable, maxComponentPoints), maxpTable->maxComponentPoints);
    TTF_READ(ttfData, offsetof(MaxpTable, maxComponentContours), maxpTable->maxComponentContours);
    TTF_READ(ttfData, offsetof(MaxpTable, maxTwilightPoints), maxpTable->maxTwilightPoints);
    TTF_READ(ttfData, offsetof(MaxpTable, maxStorage), maxpTable->maxStorage);
    TTF_READ(ttfData, offsetof(MaxpTable, maxFunctionDefs), maxpTable->maxFunctionDefs);
    TTF_READ(ttfData, offsetof(MaxpTable, maxInstructionDefs), maxpTable->maxInstructionDefs);
    TTF_READ(ttfData, offsetof(MaxpTable, maxStackElements), maxpTable->maxStackElements);
    TTF_READ(ttfData, offsetof(MaxpTable, maxSizeOfInstructions), maxpTable->maxSizeOfInstructions);
    TTF_READ(ttfData, offsetof(MaxpTable, maxComponentElements), maxpTable->maxComponentElements);
    TTF_READ(ttfData, offsetof(MaxpTable, maxComponentDepth), maxpTable->maxComponentDepth);

    return status;
}

TTFStatus getCmapInfo(TTFInfo *ttfInfo, CmapInfo *cmapInfo) {
    TTFStatus status = moveToSpecificTableDir(ttfInfo, TAG_CMAP);
    if (TTF_ERR(status)) {
        return status;
    }

    cmapInfo->ttfData = ttfInfo->ttfData + ttfInfo->cacheTableDir.offset;
    CmapIndex cmapIndex;
    TTF_READ(cmapInfo->ttfData, offsetof(CmapIndex, version),
             cmapIndex.version);
    if (cmapIndex.version != 0) {
        return UNSUPPORTED_VERSION;
    }
    TTF_READ(cmapInfo->ttfData, offsetof(CmapIndex, numsSubTables),
             cmapIndex.numsSubTables);
    cmapInfo->numsTables = cmapIndex.numsSubTables;
    moveToNthCmapSubTable(cmapInfo, 0);
    // 增加引用计数
    cmapInfo->ttfInfo = ttfInfo;
    ttfInfo->rc += 1;

    return status;
}

TTFStatus moveToNthCmapSubTable(CmapInfo *cmapInfo, size_t n) {
    if (n > cmapInfo->numsTables) {
        return OUT_OF_BOUNDARY;
    }
    size_t base = sizeof(CmapIndex) + n * sizeof(CmapSubTable);
    TTF_READ(cmapInfo->ttfData, base + offsetof(CmapSubTable, platformID),
             cmapInfo->cacheSubTable.platformID);
    TTF_READ(cmapInfo->ttfData, base + offsetof(CmapSubTable, platformSpecificID),
             cmapInfo->cacheSubTable.platformSpecificID);
    TTF_READ(cmapInfo->ttfData, base + offsetof(CmapSubTable, offset),
             cmapInfo->cacheSubTable.offset);

    return SUCCESS;
}

uint16_t getCmapSubTableFormat(CmapInfo *cmapInfo) {
    uint16_t format;
    TTF_READ(cmapInfo->ttfData, cmapInfo->cacheSubTable.offset, format);
    return format;
}

TTFStatus cmapReadAsFormat4(CmapInfo *cmapInfo, CmapFormat4Info *format4Info) {
    uint16_t format;
    size_t   base = cmapInfo->cacheSubTable.offset;
    // 先解析 format, 检查是否匹配
    TTF_READ(cmapInfo->ttfData, base, format);
    if (format != CMAP_FORMAT_4) {
        return FORMAT_MISMATCHED;
    }
    // 解析剩余字段
    base += 2;  // 跳过 format 字段
    TTF_READ(cmapInfo->ttfData, base + offsetof(CmapFormat4, length),
             format4Info->format4Table.length);
    TTF_READ(cmapInfo->ttfData, base + offsetof(CmapFormat4, lang),
             format4Info->format4Table.lang);
    TTF_READ(cmapInfo->ttfData, base + offsetof(CmapFormat4, segCount),
             format4Info->format4Table.segCount);
    format4Info->format4Table.segCount >>= 2; // 注意 ttfData 的原数据是 segCount * 2
    TTF_READ(cmapInfo->ttfData, base + offsetof(CmapFormat4, searchRange),
             format4Info->format4Table.searchRange);
    TTF_READ(cmapInfo->ttfData, base + offsetof(CmapFormat4, entrySelector),
             format4Info->format4Table.entrySelector);
    TTF_READ(cmapInfo->ttfData, base + offsetof(CmapFormat4, rangeShift),
             format4Info->format4Table.rangeShift);
    // 开始解析引用 ttfData 的数据
    base += 2 * 6; // 上面解析了六个字段
    uint16_t segCount = format4Info->format4Table.segCount;
    format4Info->format4Table.endCode = cmapInfo->ttfData + base;
    base += 2 * (segCount + 1); // endCode 长度 + reservedPad 长度
    format4Info->format4Table.startCode = cmapInfo->ttfData + base;
    base += 2 * segCount; // startCode 长度
    format4Info->format4Table.idDelta = cmapInfo->ttfData + base;
    base += 2 * segCount; // idDelta 长度
    format4Info->format4Table.idRangeOffset = cmapInfo->ttfData + base;
    base += 2 * segCount; // idRangeOffset 长度
    format4Info->format4Table.glyphIndexArray = cmapInfo->ttfData + base;

    // 增加引用计数
    format4Info->ttfInfo = cmapInfo->ttfInfo;
    format4Info->ttfInfo->rc += 1;
    return SUCCESS;
}

// 解析过程参照: https://developer.apple.com/fonts/TrueType-Reference-Manual/RM06/Chap6cmap.html
TTFStatus format4SearchGlyphIndex(CmapFormat4Info *format4Info, wchar_t c, uint16_t *glyphIndex) {
    uint16_t c_big_endian = TTF_UINT16_SWAP_BYTE(c);
    int idx = lowerBoundBigEndian(format4Info->format4Table.endCode, c_big_endian, format4Info->format4Table.segCount);
    if (idx == -1) {
        return MISSING_CHAR_GLYPH;
    }

    uint16_t startCountI = format4Info->format4Table.startCode[idx];
    if (TTF_COM_UINT(startCountI, c_big_endian) == -1) {
        return MISSING_CHAR_GLYPH;
    }

    uint16_t idRangeOffsetI = format4Info->format4Table.idRangeOffset[idx];

    if (idRangeOffsetI == 0) {
        uint32_t idDeltaBigEndian = format4Info->format4Table.idDelta[idx];
        *glyphIndex = ((uint32_t)c + TTF_UINT16_SWAP_BYTE(idDeltaBigEndian)) & 0xffff;
    } else {
        uint16_t glyphIndexBigEndian = *(uint16_t*)(
                (char*)(&format4Info->format4Table.idRangeOffset[idx]) +
                TTF_UINT16_SWAP_BYTE(idRangeOffsetI) +
                ((c - TTF_UINT16_SWAP_BYTE(startCountI)) << 1)
        );
        *glyphIndex = TTF_UINT16_SWAP_BYTE(glyphIndexBigEndian);
    }

    return SUCCESS;
}

TTFStatus getLocaInfo(TTFInfo *ttfInfo, LocaInfo *locaInfo, int16_t locaType) {
    TTFStatus status = moveToSpecificTableDir(ttfInfo, TAG_LOCA);

    if (TTF_ERR(status)) {
        return status;
    }

    switch (locaType) {
        case LOCA_FORMAT_SHORT:
        case LOCA_FORMAT_LONG:
            locaInfo->locaType = locaType;
            locaInfo->locaData = ttfInfo->ttfData + ttfInfo->cacheTableDir.offset;
            locaInfo->ttfInfo  = ttfInfo;
            ttfInfo->rc += 1;
            return SUCCESS;
        default:
            return INVALID_PARAMETER;
    }
}

TTFStatus getGlyphLocation(LocaInfo *locaInfo, uint16_t glyphIndex, GlyphLocation *glyphLocation) {
    switch (locaInfo->locaType) {
        case LOCA_FORMAT_SHORT: {
            uint16_t *locaData = locaInfo->locaData;
            glyphLocation->offset = TTF_UINT16_SWAP_BYTE(locaData[glyphIndex]);
            glyphLocation->length = TTF_UINT16_SWAP_BYTE(locaData[glyphIndex + 1]) - glyphLocation->offset;
            return SUCCESS;
        }
        case LOCA_FORMAT_LONG: {
            TTF_READ(locaInfo->locaData, glyphIndex * sizeof(uint32_t), glyphLocation->offset);
            TTF_READ(locaInfo->locaData, (glyphIndex + 1) * sizeof(uint32_t), glyphLocation->length);
            glyphLocation->length -= glyphLocation->offset;
            return SUCCESS;
        }
        default:
            return INVALID_PARAMETER;
    }
}

#endif